var clientID = Math.floor(Math.random()*9999999999);
var remoteRegisteredListeners = {"lastID" : 0};

function __selectElements(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSelectElements(root, path);
	}
			
    if (document.evaluate) {    // Firefox, Opera, Google Chrome and Safari
		var xPathRes = document.evaluate ( path, root, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
			
		var array = new Array(xPathRes.snapshotLength);
		for (i=0; i<xPathRes.snapshotLength; i++) {
			if (xPathRes.snapshotItem(i).nodeType == 1/*Node.ELEMENT_NODE*/) {
				array[i] = xPathRes.snapshotItem(i);
			}
		}
					
		return array;
    }
    else {  // Internet Explorer
        alert("Your browser does not support the evaluate method!");
		//TODO alternative implementation
    }

}

function __selectAttributes(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSelectAttributes(root, path);
	}

	var elems = __selectElements(root, path);

	var array = new Array(elems.length);
	
	for (i=0; i<elems.length; i++) {
	
		var map = new Object();
		
		if (elems[i].nodeType == 1/*Node.ELEMENT_NODE*/) {
			for (j=0; j<elems[i].attributes.length; j++) {
				 map[elems[i].attributes[j].name] = elems[i].attributes[j].value;
			}
		}
		
		array[i] = map;
	}
	
	return array;

}

function __selectContents(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSelectContents(root, path);
	}

	var elems = __selectElements(root, path);
	
	var array = new Array(elems.length);
	
	for (i=0; i<elems.length; i++) {
	
		if (elems[i].nodeType == 1/*Node.ELEMENT_NODE*/) {
			array[i] = elems[i].innerHTML;
		}
		
	}
	
	return array;
}

function __setAttributes(root, path, attributes) {
	if (typeof attributes != "object") return false;
	
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSetAttributes(root, path, attributes);
	}

	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
	
		if (elems[i].nodeType == 1/*Node.ELEMENT_NODE*/) {
		
			for (var j in attributes) {
				
				if (event.initMutationEvent) {  // all browsers except IE before version 9
					var eventObject = document.createEvent('MutationEvent');
					var attrChange;
					if (elems[i].hasAttribute(j)) {
						attrChange = MutationEvent.MODIFICATION;
					} else {
						attrChange = MutationEvent.ADDITION;
					}
					eventObject.initMutationEvent ("DOMAttrModified", false, true, attributes[j], elems[i].getAttribute(j), attributes[j], j, attrChange);
					elems[i].dispatchEvent(eventObject);
				} else {  // IE before version 9
					//TODO alternative implementation
				}
				
				elems[i].setAttribute(j, attributes[j]);
			}
			
		}
		
	}
	
	return true;
}

function __setContent(root, path, content) {
	if (typeof content == "string") {
		var newContent = content;
	} else if (content.nodeType == 1/*Node.ELEMENT_NODE*/) {
		var newContent = content.outerHTML;
	} else {
		return false;
	}
	
	if (root.nodeName == 'HYPERNODE') {
		return __remoteSetContent(root, path, newContent);
	}

	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
		
		if (elems[i].nodeType == 1/*Node.ELEMENT_NODE*/) {
		
			if (event.initMutationEvent) {  // all browsers except IE before version 9
				var eventObject = document.createEvent('MutationEvent');
				eventObject.initMutationEvent ("DOMContentModified", true, true, elems[i], elems[i].innerHTML, newContent, null, MutationEvent.MODIFICATION);
				elems[i].dispatchEvent(eventObject);
			} else {  // IE before version 9
					//TODO alternative implementation
			}
				
			elems[i].innerHTML = newContent;
			
		}
		
	}
	
	return true;
}

function __removeElements(root, path) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteRemoveElements(root, path);
	}
	
	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
		if (elems[i].nodeType == 1/*Node.ELEMENT_NODE*/) {
			elems[i].parentNode.removeChild(elems[i]);
		}
	}
	
	return true;
}

function __registerEventListener(root, path, eventType, fun) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteRegisterEventListener(root, path);
	}

	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
		if (elems[i].parentNode) {
			if (elems[i].addEventListener) {  // all browsers except IE before version 9
				elems[i].addEventListener(eventType, fun);
			} else {  // IE before version 9
				//TODO alternative implementation (if any)
			}
		}
	}
	
	return true;
}

function __unregisterEventListener(root, path, eventType, fun) {
	if (root.nodeName == 'HYPERNODE') {
		return __remoteUnregisterEventListener(root, path);
	}
	
	var elems = __selectElements(root, path);
	
	for (i=0; i<elems.length; i++) {
	//TODO check that elems[i] is a node
		if (button.removeEventListener) {  // all browsers except IE before version 9
			elems[i].removeEventListener(eventType, fun);
		} else {  // IE before version 9
			//TODO alternative implementation (if any)
		}
	}
}

function __remoteSelectElements(root, path) {

	var elems = __ajaxRequest({ "op" : "selectElements",
							    "root" : root.getAttribute('refname'),
								"path" : path },
							  root.getAttribute('refspace') == 'application');
	
	for (i=0; i<elems.length; i++) {
			var newNode = document.createElement(elems[i].name);
			newNode.outerHTML = elems[i].html;
			elems[i] = newNode;
	}
	
	return elems;
}

function __remoteSelectAttributes(root, path) {

	return __ajaxRequest({ "op" : "selectAttributes",
						   "root" : root.getAttribute('refname'),
						   "path" : path },
						 root.getAttribute('refspace') == 'application');
	
}

function __remoteSelectContents(root, path) {

	return __ajaxRequest({ "op" : "selectContents",
						   "root" : root.getAttribute('refname'),
						   "path" : path },
						 root.getAttribute('refspace') == 'application');
						 
}

function __remoteSetAttributes(root, path, attributes) {

	return __ajaxRequest({ "op" : "setAttributes",
						   "root" : root.getAttribute('refname'),
						   "path" : path, "attributes" : attributes },
						 root.getAttribute('refspace') == 'application');
	
}

function __remoteSetContent(root, path, content) {

	return __ajaxRequest({ "op" : "setContent",
						   "root" : root.getAttribute('refname'),
						   "path" : path, "content" : content },
						 root.getAttribute('refspace') == 'application');
	
}

function __remoteRemoveElements(root, path) {

	return __ajaxRequest({ "op" : "removeElements",
						   "root" : root.getAttribute('refname'),
						   "path" : path },
						 root.getAttribute('refspace') == 'application');
	
}

function __remoteRegisterEventListener(root, path, eventType, fun) {

	remoteRegisteredListeners.lastID++;
	remoteRegisteredListeners["lastID"] = {"fun" : fun, "root" : root};

	return __ajaxRequest({ "op" : "registerEventListener",
						   "root" : root.getAttribute('refname'),
						   "path" : path,
						   "eventType" : eventType,
						   "funID" : lastID},
						 root.getAttribute('refspace') == 'application');
	
}

function __remoteUnregisterEventListener(root, path, eventType, fun) {

	for (var i in remoteRegisteredListeners) {
	
		if (i == fun) {
		
			remoteRegisteredListeners[i] = null;
			
			return __ajaxRequest({ "op" : "unregisterEventListener",
								   "root" : root.getAttribute('refname'),
								   "path" : path,
									"eventType" : eventType,
									"funID" : i},
								 root.getAttribute('refspace') == 'application');
			
		}
		
	}

	
	
}

function __ajaxRequest(msg, bApplication) {
	var ret;
	
	msg["clientID"] = clientID;
	
	var serverPage;
	
	if (bApplication)
		serverPage = 'application_server.sjs';
	else 
		serverPage = 'session_server.sjs';
	
	$.ajax(
		      {
		        url: "http://localhost/"+ serverPage,
		        dataType: 'json',
		        data: msg,
				async: false,
		        success: function(data) {
		            ret = data.result;

					for (i=0; i<ret.events; i++) {
						ret.events[i].arg.relatedRoot = remoteRegisteredListeners[ret.events[i].funID].root;
						var newNode = document.createElement(ret.events[i].arg.relatedNodeName);
						newNode.outerHTML = ret.events[i].arg.relatedNode;
						ret.events[i].arg.relatedNode = newNode;
						remoteRegisteredListeners[ret.events[i].funID].fun(ret.events[i].arg);
					}
		        }
		      }
		    )

	return ret;
}
